home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Diamond Collection / The Diamond Collection (Software Vault)(Digital Impact).ISO / cdr13 / jed10.zip / JED.PAS < prev    next >
Pascal/Delphi Source File  |  1989-04-24  |  47KB  |  1,253 lines

  1. {--------------------------------------------------------------}
  2. {                           JED                                }
  3. {                                                              }
  4. {  Jeff's Editor & Assembly Language Development Environment   }
  5. {                                                              }
  6. {                             by Jeff Duntemann                }
  7. {                             Turbo Pascal V5.00               }
  8. {                             Last update 4/19/89              }
  9. {                                                              }
  10. {               (c) 1989 by Jeff Duntemann                     }
  11. {      Binary Editor module (c) 1988 Borland International     }
  12. {--------------------------------------------------------------}
  13.  
  14. { Version 1.01 -- Rudimentary file error capture }
  15.  
  16. {$M 16384,8192,148000}
  17.  
  18.  
  19. PROGRAM JED;
  20.  
  21. { Note well that this program REQUIRES Turbo Pascal 5.0! }
  22.  
  23. USES
  24.   Bined,     { From the Turbo Pascal Editor Toolbox V4.0 }
  25.   CRT,       { Standard Borland unit }
  26.   DOS,       { Standard Borland unit }
  27.   TextInfo;  { By Jeff Duntemann; published in DDJ 3/89  }
  28.  
  29. TYPE
  30.   String80         = STRING[80];
  31.  
  32. CONST
  33.   UP               = True;      { For forcing strings to uc/lc }
  34.   DOWN             = False;
  35.   ConfigFileName   = 'JED.CFG';
  36.   DefaultExtension = '.ASM';
  37.   SUBCHAR          = '~';
  38.   BlackOnWhite     = $70;       { Reverse video attribute' color or mono }
  39.  
  40.   {Coordinates of the editor window}
  41.   Windx1 = 1;
  42.   Windy1 = 1;
  43.   Windx2 = 80;
  44.   Windy2 = 25;         { 43 for EGA; 50 for VGA; 66 for Genius }
  45.   MakeBackup = True;   { When True, JED creates .BAK files }
  46.  
  47.   {Commands other than ^K^D to exit editor}
  48.   ExitCommands : array[0..33] of Char =
  49.   (#2, ^K, ^Q,        { Ctrl-KQ: Exit without saving file }
  50.    #2, #0, #33,       { Alt-F:   Change work file  }
  51.    #2, #0, #45,       { Alt-X:   Save and exit     }
  52.    #2, #0, #59,       { F1:      Show help screen  }
  53.    #2, #0, #60,       { F2:      Save current file }
  54.    #2, #0, #61,       { F3:      Invoke DEBUG on current .EXE file }
  55.    #2, #0, #62,       { F4:      Update assemble/link command line }
  56.    #2, #0, #63,       { F5:      Exec to DOS command line }
  57.    #2, #0, #64,       { F6:      Examine last Exec screen }
  58.    #2, #0, #67,       { F9:      Assemble only }
  59.    #2, #0, #68,       { F10:     Simple MAKE: Assemble, link, and go }
  60.    #0);
  61.  
  62.  
  63.  
  64. TYPE
  65.   ScreenSaveRec = RECORD
  66.                     SaveX,SaveY : Integer;
  67.                     SavePtr     : Pointer
  68.                   END;
  69.  
  70.   ConfigRec     = RECORD
  71.                     Workfile        : String80;
  72.                     CursorInset     : Word;      { Cursor X,Y at last save }
  73.                     AssembleCommand : String80;  { Command with switches   }
  74.                     LinkCommand     : String80;  { Ditto for linker        }
  75.                     TestParms       : String80   { Any parameters for prog }
  76.                   END;                        { under development with JED }
  77.  
  78.   ConfigFile    = FILE OF ConfigRec;
  79.  
  80.  
  81. CONST
  82.   ConfigData    : ConfigRec =
  83.                   (Workfile        : 'NONAME.ASM';
  84.                    CursorInset     : 0;
  85.                    AssembleCommand : 'TASM ~';
  86.                    LinkCommand     : 'TLINK ~';
  87.                    TestParms       : '');
  88.  
  89.  
  90.  
  91.  
  92. VAR
  93.   EdData       : EdCB;           { Editor control block }
  94.   ExitCode     : Word;           { Status code set by bin. ed. functions }
  95.   ExitCommand  : Integer;        { Code for command used to leave editor }
  96.   Fname        : STRING;         { Input name of file being edited  }
  97.   TempName     : STRING;         { Holds name while changing files  }
  98.   Quit         : Boolean;        { Ends program }
  99.   DOSScreen    : ScreenSaveRec;  { Saves DOS screen under JED       }
  100.   JEDScreen    : ScreenSaveRec;  { Saves JED screen under help or exec }
  101.   ExecScreen   : ScreensaveRec;  { Saves Exec screen for later examination }
  102.   BarAttribute : Byte;           { Video attribute for prompt bar }
  103.   Now          : DateTime;       { For the clock display }
  104.   ConfigStore  : ConfigFile;     { Contains configuration data on disk }
  105.   UpdateConfigData : Boolean;    { If True, update JED.CFG on exit }
  106.  
  107.  
  108. {-------------------------------------------------------------------------}
  109. { The following EXTERNAL definitions are *not* code, but screen patterns  }
  110. { stored as external assembly language procedures.  They are put to the   }
  111. { screen using the VidBlast external machine code procedure.  DO NOT TRY  }
  112. { TO EXECUTE THEM!  Bizarre machine behavior including lockup WILL occur. }
  113. {-------------------------------------------------------------------------}
  114.  
  115. {$L JEDSCRN}
  116. {$F+}
  117. PROCEDURE JEDHelp; EXTERNAL; { JED's help screen }
  118. PROCEDURE JEDBar;  EXTERNAL; { The prompt bar at the bottom of the screen }
  119. PROCEDURE JEDFile; EXTERNAL; { The file name entry box invoked with Alt-F }
  120. PROCEDURE JEDErr;  EXTERNAL; { The JED error message box }
  121. {$F-}
  122.  
  123.  
  124. {$L VIDBLAST}
  125. {$F+}
  126. PROCEDURE VidBlast(ScreenEnd,StoreEnd : Pointer;
  127.                    ScreenX,ScreenY    : Integer;
  128.                    ULX,ULY            : Integer;
  129.                    Width,Height       : Integer;
  130.                    Attribute          : Byte;
  131.                    DeadLines          : Integer);
  132.           EXTERNAL;
  133. {$F-}
  134.  
  135.  
  136. {-UhUh-------------------------------------------------------------}
  137. {                                                                  }
  138. { Bored with beeps?  Try this one..the name is very characteristic }
  139. { of the sound.                                                    }
  140. {------------------------------------------------------------------}
  141.  
  142. PROCEDURE UhUh;
  143.  
  144. VAR
  145.   I : Integer;
  146.  
  147. BEGIN
  148.   FOR I := 1 TO 2 DO
  149.     BEGIN
  150.       Sound(50);
  151.       Delay(100);
  152.       NoSound;
  153.       Delay(50)
  154.     END
  155. END;
  156.  
  157.  
  158. PROCEDURE StripWhite(VAR Target : String80);
  159.  
  160. CONST
  161.   WhiteSpace : SET OF Char = [#7,#8,#10,#9,#12,#13,' '];
  162.  
  163. BEGIN
  164.   WHILE (Length(Target) > 0 ) AND (Target[1] IN Whitespace) DO
  165.     Delete(Target,1,1);
  166. END;
  167.  
  168.  
  169. {-ForceCase--------------------------------------------------------}
  170. {                                                                  }
  171. { When Up is True, Target is forced to all upper case.  When Up is }
  172. { False, Target is forced to all lower case.                       }
  173. {------------------------------------------------------------------}
  174.  
  175. FUNCTION ForceCase(Up : Boolean; Target : STRING) : STRING;
  176.  
  177. CONST
  178.   Uppercase : SET OF Char = ['A'..'Z'];
  179.   Lowercase : SET OF Char = ['a'..'z'];
  180.  
  181. VAR
  182.   I : INTEGER;
  183.  
  184. BEGIN
  185.   IF Up THEN FOR I := 1 TO Length(Target) DO
  186.     IF Target[I] IN Lowercase THEN
  187.       Target[I] := UpCase(Target[I])
  188.     ELSE { NULL }
  189.   ELSE FOR I := 1 TO Length(Target) DO
  190.     IF Target[I] IN Uppercase THEN
  191.       Target[I] := Chr(Ord(Target[I])+32);
  192.   ForceCase := Target
  193. END;
  194.  
  195.  
  196. {-WriteColor-------------------------------------------------------}
  197. {                                                                  }
  198. { The trick here is to save the current screen attribute, (kept in }
  199. { the variable TextAttr exported by the Crt unit) set TextAttr to  }
  200. { the attribute passed in InColor, write WriteData to the screen,  }
  201. { and finally restore the contents of TextAttr that were in force  }
  202. { when WriteColor took control.                                    }
  203. {------------------------------------------------------------------}
  204.  
  205. PROCEDURE WriteColor(InColor : Byte; WriteData : String);
  206.  
  207. VAR
  208.   SaveAttr : Byte;
  209.  
  210. BEGIN
  211.   SaveAttr := Crt.TextAttr;
  212.   Crt.TextAttr := InColor;
  213.   Write(WriteData);
  214.   Crt.TextAttr := SaveAttr
  215. END;
  216.  
  217.  
  218. {-SaveScreenOut-and-BringScreenBack--------------------------------}
  219. {                                                                  }
  220. { These two routines are inverses of one another.  SaveScreenOut   }
  221. { allocates space on the heap and saves the displayed text buffer  }
  222. { into the allocated space.  The current cursor position is saved  }
  223. { in the ScreenSaveRec parameter, and the position is reasserted   }
  224. { when the screen is moved back into the video refresh buffer with }
  225. { BringScreenBack.  The number of bytes moved is determined by the }
  226. { TextBufferSixe variable exported by the TextInfo unit.  The      }
  227. { generic pointer TextBufferOrigin is also exported by TextInfo.   }
  228. {------------------------------------------------------------------}
  229.  
  230. PROCEDURE SaveScreenOut(VAR OutboundScreen : ScreenSaveRec);
  231.  
  232. BEGIN
  233.   WITH OutboundScreen DO
  234.     BEGIN
  235.       SaveX := WhereX; SaveY := WhereY;    { Save the underlying cursor pos. }
  236.       { Allocate memory for stored screen: }
  237.       GetMem(SavePtr,TextBufferSize);
  238.       { Save screen out to the heap:  }
  239.       Move(TextBufferOrigin^,SavePtr^,TextBufferSize);
  240.     END
  241. END;
  242.  
  243.  
  244. PROCEDURE BringScreenBack(VAR InboundScreen : ScreenSaveRec);
  245.  
  246. BEGIN
  247.   WITH InboundScreen DO
  248.     BEGIN
  249.       Move(SavePtr^,TextBufferOrigin^,TextBufferSize);  { Bring screen back  }
  250.       FreeMem(SavePtr,TextBufferSize);   { Free up the meap memory          }
  251.       SavePtr := NIL;
  252.       GotoXY(SaveX,SaveY);               { Put the cursor back where it was }
  253.     END
  254. END;
  255.  
  256.  
  257. {-WaitForAnyKeystroke----------------------------------------------}
  258. {                                                                  }
  259. { All this does is print a centered prompt on the last screen line }
  260. { and wait for a keystroke.                                        }
  261. {------------------------------------------------------------------}
  262.  
  263. PROCEDURE WaitForAnyKeystroke;
  264.  
  265. VAR
  266.   Dummy : Char;
  267.  
  268. BEGIN
  269.   GotoXY(20,VisibleY); Write('Press any key to return to JED...');
  270.   REPEAT UNTIL KeyPressed;             { Wait for a keystroke }
  271.   Dummy := ReadKey;                    { Go get pressed key }
  272.   IF Dummy = Chr(0) THEN Dummy := ReadKey;
  273. END;
  274.  
  275.  
  276. {-GetString--------------------------------------------------------}
  277. {                                                                  }
  278. { Here's your generic field editor.  Pass the string to be edited  }
  279. { in XString, the location of the left character of the field in X }
  280. { and Y, the maximum length allowable in MaxLen, the attribute for }
  281. { foreground/background colors in UseColor, and nothing in         }
  282. { ESCPressed--that's a return value, indicating that the user hit  }
  283. { the ESC key.  XString will be displayed left-justified in the    }
  284. { field, but if the first character pressed is a printable one,    }
  285. { the field will be blanked, allowing for rapid entry of new       }
  286. { strings.  Note that if ESC is pressed, XSTRing is not altered.   }
  287. {------------------------------------------------------------------}
  288.  
  289. PROCEDURE GetString(X,Y             : Integer;
  290.                     VAR XString     : String80;
  291.                     MaxLen          : Integer;
  292.                     UseColor        : Byte;
  293.                     VAR EscPressed  : Boolean);
  294.  
  295. CONST  Dot        : Char = '.';
  296.        Printables : SET OF Char = [' '..'~'];
  297.  
  298. VAR I,J        : Integer;
  299.     Ch         : Char;
  300.     ClearIt    : String80;
  301.     Worker     : String80;
  302.     GotChar    : Boolean;
  303.     CR         : Boolean;
  304.     Virgin     : Boolean;
  305.  
  306. BEGIN
  307.   CR := False; EscPressed := False; Virgin := True;
  308.   FillChar(ClearIt,SizeOf(ClearIt),'.');  { Fill the clear string  }
  309.   ClearIt[0] := Chr(MaxLen);              { Set clear string to MaxLen }
  310.  
  311.   { Truncate string value to MaxLen: }
  312.   IF Length(XString) > MaxLen THEN XString[0] := Chr(MaxLen);
  313.   GotoXY(X,Y); WriteColor(UseColor,ClearIt);    { Draw the field  }
  314.   GotoXY(X,Y); WriteColor(UseColor,XString);
  315.   IF Length(XString) < MaxLen THEN
  316.     GotoXY(X + Length(XString),Y);
  317.  
  318.   Worker := XString;      { Fill work string with input string     }
  319.  
  320.   REPEAT                  { Until ESC or (CR) entered }
  321.                           { Wait here for keypress:   }
  322.     REPEAT
  323.       GotChar := True;
  324.       WHILE NOT KeyPressed DO BEGIN {NULL} END;
  325.       Ch := ReadKey;
  326.       IF Ord(CH) = 0 THEN  { If an extended keycode was received..   }
  327.         BEGIN
  328.           Ch := Readkey;   { ..get the other half of it to ignore it }
  329.           GotChar := False { Set the flag so we loop & get another   }
  330.         END
  331.     UNTIL GotChar;
  332.  
  333.     IF Ch IN Printables THEN                    { If Ch is printable... }
  334.       BEGIN
  335.         IF Virgin THEN  { We clear the field if first char is printable }
  336.           BEGIN
  337.             Worker := '';
  338.             GotoXY(X,Y);
  339.             WriteColor(UseColor,Clearit);    { Fill the field with dots }
  340.             Virgin := False;
  341.           END;
  342.         IF Length(Worker) >= MaxLen THEN UhUh ELSE   { If we're full... }
  343.           BEGIN
  344.             Worker := CONCAT(Worker,Ch);    { Append it to the work string  }
  345.             GotoXY(X,Y); WriteColor(UseColor,Worker);   { and redisplay it  }
  346.             IF Length(Worker) >= MaxLen THEN  { Keep hardware cursor within }
  347.               GotoXY(X+MaxLen-1,Y);             { the field }
  348.           END
  349.       END
  350.     ELSE   { If Ch is NOT printable... }
  351.       BEGIN
  352.         Virgin := False;
  353.         CASE Ord(Ch) OF
  354.          8,127 : IF Length(Worker) <= 0 THEN UhUh ELSE { Backspace & rubout }
  355.                   BEGIN
  356.                     Delete(Worker,Length(Worker),1);
  357.                     GotoXY(X,Y); WriteColor(UseColor,Worker);
  358.                     IF Length(Worker) < MaxLen THEN WriteColor(UseColor,Dot);
  359.                     GotoXY(X+Length(Worker),Y);
  360.                   END;
  361.  
  362.          13 : CR := True;          { Carriage return; keep changes }
  363.  
  364.          24 : BEGIN                { CTRL-X : Blank the field }
  365.                 GotoXY(X,Y); WriteColor(UseColor,ClearIt);
  366.                 GotoXY(X,Y);
  367.                 Worker := '';      { Blank out work string }
  368.               END;
  369.  
  370.          27 : EscPressed := True;  { ESC; abandon changes }
  371.          ELSE UhUh                 { CASE ELSE; no other legal control chars }
  372.         END; { CASE }
  373.       END
  374.   UNTIL CR OR EscPressed;        { Get keypresses until (CR) or }
  375.                                  { ESC pressed }
  376.  
  377.   IF CR THEN XString := Worker;  { Don't update XString if ESC hit }
  378.  
  379. END;  { GetString }
  380.  
  381.  
  382.  
  383. PROCEDURE WriteStatus(msg : string);
  384.   {-Write a status message}
  385.  
  386. BEGIN                       {WriteStatus}
  387.   GoToXY(1, Windy2);
  388.   TextColor(White);
  389.   Write(msg);
  390. END;                        {WriteStatus}
  391.  
  392.  
  393.  
  394. PROCEDURE ShowJEDErrorMessage(ErrX,ErrY : Integer; Message : STRING);
  395.  
  396. BEGIN
  397.   VidBlast(TextBufferOrigin,@JEDBar,  { Blast in the JED error frame     }
  398.            VisibleX,VisibleY,         { Dimensions of current screen     }
  399.            ErrX,ErrY,                 { Load it at bottom screen line    }
  400.            62,5,                      { JEDErr is 62 wide and 5 high     }
  401.            07,                        { Use the normal attribute         }
  402.            0);                        { No interspersed blank lines      }
  403.   GotoXY(ErrX+3,ErrY+2);
  404.   Write(Message);
  405. END;
  406.  
  407.  
  408.  
  409. PROCEDURE SaveConfigFile(ConfigData : ConfigRec);
  410.  
  411. BEGIN
  412.   { Save the last known cursor inset into the edited file: }
  413.   ConfigData.CursorInset := EdData.CursorPos;
  414.   Assign(ConfigStore,ConfigFileName);
  415.   Rewrite(ConfigStore);
  416.   Write(ConfigStore,ConfigData);
  417.   Close(ConfigStore)
  418. END;
  419.  
  420.  
  421. {-GetFileName------------------------------------------------------}
  422. {                                                                  }
  423. { This routine is called when JED starts up, and it returns a file }
  424. { name to load and edit.  It first looks on the parameter line for }
  425. { a file name.  If parameters were entered, the configuration file }
  426. { is opened, and the name of the last file saved will be loaded    }
  427. { and used.  If the config file can't be read, NONAME.ASM will be  }
  428. { used as a filename.                                              }
  429. {------------------------------------------------------------------}
  430.  
  431. FUNCTION GetFileName(VAR ConfigData : ConfigRec) : STRING;
  432.  
  433. VAR
  434.   TempConfigData : ConfigRec;
  435.   TempName       : String80;
  436.   I              : Integer;
  437.  
  438.  
  439. PROCEDURE ReadConfigFile(VAR ConfigFromDisk : ConfigRec);
  440.  
  441. BEGIN
  442.   Assign(ConfigStore,ConfigFileName);
  443.   {$I-} Reset(ConfigStore); {$I+}
  444.   { IF JED.CFG can't be read, reassert defaults: }
  445.   IF IOResult <> 0 THEN
  446.     BEGIN
  447.       WITH ConfigData DO
  448.         BEGIN
  449.           WorkFile        := 'NONAME.ASM';
  450.           CursorInset     := 0;
  451.           AssembleCommand := 'TASM ~';
  452.           LinkCommand     := 'TLINK ~';
  453.           TestParms       := ''
  454.         END
  455.     END
  456.   ELSE  { Read JED.CFG from disk }
  457.     BEGIN
  458.       Read(ConfigStore,ConfigData);
  459.       Close(ConfigStore);
  460.     END
  461. END;
  462.  
  463.  
  464.  
  465. BEGIN  { GetFileName }
  466.   IF ParamCount > 0 THEN   { If there are parms, read #1 as file name }
  467.     BEGIN
  468.       TempName := ParamStr(1);        { Save command parm #1 in temp string }
  469.       { Force the name to upper case: }
  470.       TempName := Forcecase(UP,TempName);
  471.       { If the name has no extentions, append the default extension: }
  472.       IF Pos('.',TempName) = 0 THEN
  473.         TempName := TempName + DefaultExtension;
  474.       ReadConfigFile(TempConfigData); { Read JED.CFG from disk }
  475.       { If the workfile name in JED.CFG matches parm #1, use rest of JED.CFG }
  476.       IF TempName = TempConfigData.WorkFile THEN
  477.         Configdata := TempConfigData
  478.       ELSE                            { Otherwise, reassert defaults for }
  479.         WITH ConfigData DO            { config data other than work file }
  480.           BEGIN
  481.             WorkFile        := TempName;
  482.             CursorInset     := 0;
  483.             AssembleCommand := 'TASM ~';
  484.             LinkCommand     := 'TLINK ~';
  485.             TestParms       := ''
  486.           END
  487.     END
  488.   ELSE ReadConfigFile(ConfigData);    { No parms; use full JED.CFG data }
  489.   GetFileName := ConfigData.WorkFile;
  490. END;
  491.  
  492.  
  493. {-RequestFileName--------------------------------------------------}
  494. {                                                                  }
  495. { If the user needs to change files within a JED session, this     }
  496. { routine takes care of prompting for a new file name.  If Enter   }
  497. { is pressed after field entry, the name entered in the field is   }
  498. { returned.  If ESC is pressed instead, the name in the config     }
  499. { file is returned instead and is usually the file being edited.   }
  500. {------------------------------------------------------------------}
  501.  
  502. FUNCTION RequestFileName(ConfigInfo : ConfigRec) : String;
  503.  
  504. CONST
  505.   BoxX = 20;
  506.   BoxY = 5;
  507.  
  508. VAR
  509.   ESCPressed : Boolean;
  510.   TempName   : String80;
  511.  
  512. BEGIN
  513.   ESCPressed := False;
  514.   SaveScreenOut(JEDScreen); { Save the underlying screen out to the heap }
  515.  
  516.   VidBlast(TextBufferOrigin,@JEDFile, { Blast in the JED change-file box }
  517.            VisibleX,VisibleY,         { Dimensions of current screen     }
  518.            BoxX,BoxY,                 { Put it at the passed X,Y values  }
  519.            38,12,                     { JEDFile is 38 wide and 12 high   }
  520.            BarAttribute,              { Use an appropriate attribute     }
  521.            0);                        { No interspersed blank lines      }
  522.  
  523.   TempName := ConfigInfo.WorkFile;    { Use current file as default }
  524.   GotoXY(BoxX+19,BoxY+4); WriteColor(BlackOnWhite,FName);
  525.   GetString(BoxX+19,BoxY+6,TempName,12,BlackOnWhite,ESCPressed);
  526.   IF ESCPressed THEN   { If ESC pressed, keep the name in the config file }
  527.     RequestFileName := ConfigInfo.WorkFile
  528.   ELSE
  529.     RequestFilename := TempName;      { Return the new name }
  530.   BringScreenBack(JEDScreen);         { Bring back the underlying screen }
  531. END;
  532.  
  533.  
  534. FUNCTION GetProg(CommandLine : String80) : String80;
  535.  
  536. BEGIN
  537.   StripWhite(CommandLine);
  538.   IF Length(CommandLine) > 0 THEN
  539.     GetProg := Copy(CommandLine,1,Pos(' ',CommandLine)-1) + '.EXE'
  540.   ELSE GetProg := '';
  541. END;
  542.  
  543.  
  544. FUNCTION GetParms(CommandLine : String80;
  545.                   WorkFile    : String80) : String80;
  546.  
  547. VAR
  548.   Dir    : DirStr;    { These 3 types are defined in the DOS unit... }
  549.   Name   : NameStr;
  550.   Ext    : ExtStr;
  551.  
  552.   SubPos : Integer;
  553.  
  554. BEGIN
  555.   FSplit(WorkFile,Dir,Name,Ext);
  556.   StripWhite(CommandLine);
  557.   IF Length(CommandLine) > 0 THEN
  558.     BEGIN
  559.       Delete(CommandLine,1,Pos(' ',CommandLine));
  560.       SubPos := Pos(SUBCHAR,CommandLine);
  561.       IF SubPos = 0 THEN
  562.         CommandLine := Name + ' ' + CommandLine
  563.       ELSE
  564.         BEGIN
  565.           Delete(CommandLine,SubPos,1);
  566.           Insert(Name,CommandLine,SubPos);
  567.         END;
  568.       GetParms := CommandLine
  569.     END
  570.   ELSE
  571.     GetParms := ''
  572. END;
  573.  
  574.  
  575.  
  576. FUNCTION EXEForm(WorkFileName : String80) : String80;
  577.  
  578. BEGIN
  579.   IF Pos('.',WorkFileName) = 0 THEN EXEForm :=
  580.     WorkFileName + '.EXE'
  581.   ELSE
  582.     EXEForm := Copy(WorkFileName,1,Pos('.',WorkFileName)-1) + '.EXE';
  583. END;
  584.  
  585.  
  586. {-ShowHelp---------------------------------------------------------}
  587. {                                                                  }
  588. { When the user pressed F1, this routine gets control and blasts a }
  589. { single-screen help summary into the video refresh buffer.  It    }
  590. { will remain on display until any key is pressed.                 }
  591. {------------------------------------------------------------------}
  592.  
  593. PROCEDURE ShowHelp;    { Shows a help screen display on press of F1 }
  594.  
  595. VAR
  596.   Dummy    : Char;
  597.  
  598.  
  599. BEGIN  { ShowHelp }
  600.   SaveScreenOut(JEDScreen); { Save the underlying screen out to the heap }
  601.   ClrScr;                   { Clear what's on the visible screen         }
  602.  
  603.   VidBlast(TextBufferOrigin,@JEDHelp,  { Blast in the JED help screen    }
  604.            VisibleX,VisibleY,          { Dimensions of current screen    }
  605.            1,1,                        { Load it at screen position 1,1  }
  606.            80,24,                      { JEDHelp is 80 wide and 24 high  }
  607.            BarAttribute,               { Use an appropriate text attribute }
  608.            0);                         { No interspersed blank lines       }
  609.  
  610.   WaitForAnyKeystroke;
  611.   BringScreenBack(JEDScreen);          { Bring back the underlying screen }
  612. END;
  613.  
  614.  
  615.  
  616. PROCEDURE AssembleAndLink;
  617.  
  618. BEGIN
  619.   WITH ConfigData DO
  620.     BEGIN
  621.       Exec(GetProg(AssembleCommand),GetParms(AssembleCommand,WorkFile));
  622.       Exec(GetProg(LinkCommand),GetParms(LinkCommand,WorkFile))
  623.     END
  624. END;
  625.  
  626.  
  627.  
  628. PROCEDURE CheckInitBinary(ExitCode : Word);
  629.   {-Check the results of the editor load operation}
  630.  
  631. BEGIN                       {CheckInitBinary}
  632.   IF ExitCode <> 0 THEN
  633.     BEGIN
  634.       {Couldn't load editor}
  635.       case ExitCode of
  636.         1 : WriteStatus('Insufficient heap space for text buffer');
  637.       ELSE
  638.         WriteStatus('Unknown load error');
  639.     END;
  640.     GoToXY(1, Windy2);
  641.     Halt(1);
  642.   END;
  643. END;                        {CheckInitBinary}
  644.  
  645.  
  646. {-CheckReadFile----------------------------------------------------}
  647. {                                                                  }
  648. {------------------------------------------------------------------}
  649.  
  650. PROCEDURE CheckReadFile(ExitCode : Word; Fname : string);
  651.   {-Check the results of the file read}
  652. VAR
  653.   f : file;
  654.  
  655. BEGIN                       {CheckReadFile}
  656.   IF ExitCode <> 0 THEN
  657.     BEGIN
  658.       {Couldn't read file}
  659.       CASE ExitCode of
  660.         1 : BEGIN
  661.               {New file, assure valid file name}
  662.               {$I-}
  663.               Assign(f, Fname);
  664.               Rewrite(f);
  665.               IF IOResult <> 0 THEN
  666.                 BEGIN
  667.                   Close(f);
  668.                   WriteStatus('Illegal file name '+Fname);
  669.                 END
  670.               ELSE
  671.                 BEGIN
  672.                   Close(f);
  673.                   Erase(f);
  674.                   GotoXY(1,1);
  675.                   ClrEOL;
  676.                   Write('New File');
  677.                   Delay(2000);
  678.                   GoToXY(1,1);
  679.                   ClrEol;
  680.                   Exit;
  681.                 END;
  682.                 {$I+}
  683.             END;
  684.         2 : WriteStatus('Insufficient text buffer size');
  685.         ELSE WriteStatus('Unknown read error');
  686.       END; { CASE }
  687.       GoToXY(1,Windy2);
  688.       Halt(1);
  689.     END;
  690.   GoToXY(1,1);
  691.   ClrEol;
  692.   UpdateConfigData := True
  693. END;                        {CheckReadFile}
  694.  
  695.  
  696. {-CheckSaveFile----------------------------------------------------}
  697. {                                                                  }
  698. { <this routine is not yet complete>                               }
  699. {------------------------------------------------------------------}
  700.  
  701. PROCEDURE CheckSaveFile(ExitCode : Word; Fname : string);
  702.  
  703. BEGIN
  704.   IF ExitCode <> 0 THEN
  705.     BEGIN
  706.       {Couldn't save file}
  707.       CASE ExitCode of
  708.         1 : WriteStatus('Unable to create output file '+Fname);
  709.         2 : WriteStatus('Error while writing output to '+Fname);
  710.         3 : WriteStatus('Unable to close output file '+Fname);
  711.         ELSE WriteStatus('Unknown write error');
  712.       END; { CASE }
  713.       GoToXY(1,Windy2);
  714.       Halt(1);
  715.     END
  716.   ELSE UpdateConfigData := True;
  717. END;
  718.  
  719.  
  720. {-MustMake---------------------------------------------------------}
  721. { If this function returns True, the .EXE file is out of date and  }
  722. { must be re-MADE.  The decision is based on a comparison of the   }
  723. { source file time stamp to the .EXE file time stamp.              }
  724. {------------------------------------------------------------------}
  725.  
  726. FUNCTION MustMake(CurrentFile : String80) : Boolean;
  727.  
  728. VAR
  729.   TimeText,TimeEXE : LongInt;   { Time stamps for source & .EXE files }
  730.   Target           : File;      { Untyped file allows opening files }
  731.   IO               : Integer;
  732.  
  733. BEGIN
  734.   Assign(Target,EXEForm(CurrentFile));
  735.   {$I-} Reset(Target); {$I+}
  736.   IO := IOResult;
  737.   IF IO <> 0 THEN MustMake := True
  738.     ELSE
  739.       BEGIN
  740.         GetFTime(Target,TimeEXE); { Get time stamp of .EXE file }
  741.         Close(Target);
  742.         IF Pos('.',CurrentFile) = 0 THEN
  743.           CurrentFile := CurrentFile + DefaultExtension;
  744.         Assign(Target,CurrentFile);
  745.         {$I-} Reset(Target); {$I+}
  746.         IO := IOREsult;
  747.         IF IO <> 0 THEN MustMake := True
  748.           ELSE
  749.             BEGIN
  750.               GetFTime(Target,TimeText);  { Get time stamp of source file }
  751.               Close(Target);
  752.               IF TimeText > TimeEXE THEN MustMake := True
  753.                 ELSE MustMake := False
  754.             END
  755.       END
  756. END;
  757.  
  758.  
  759. {-Clocker----------------------------------------------------------}
  760. {                                                                  }
  761. { This proc acts as an event handler for BINED's user event        }
  762. { dispatcher.  Whenever it isn't busy doing something else, BINED  }
  763. { passes control out to an address placed in the editor control    }
  764. { block by the InitWindow proc.  The proc must be FAR and it ought }
  765. { to be pretty quick about doing whatever it does.  Here, all we   }
  766. { want to do is display the time in the upper right corner of the  }
  767. { screen, within the BINED status line.                            }
  768. {------------------------------------------------------------------}
  769.  
  770. {$F+} { All User-Event procedures must be FAR calls!}
  771. PROCEDURE Clocker(EventNo,Info : Integer);
  772.  
  773. VAR
  774.   Hours,Minutes,Seconds,Hundredths : word;
  775.   TimeBuf,TimeTemp : String;
  776.  
  777. BEGIN
  778.   GetTime(Hours,Minutes,Seconds,Hundredths);
  779.   Str(Hours:2,TimeBuf);
  780.   Str(Minutes:2,TimeTemp);
  781.   IF TimeTemp[1] = ' ' THEN TimeTemp[1] := '0';
  782.   TimeBuf := TimeBuf+':'+TimeTemp;
  783.   Str(Seconds:2,TimeTemp);
  784.   IF TimeTemp[1] = ' ' THEN TimeTemp[1] := '0';
  785.   TimeBuf := TimeBuf+':'+TimeTemp;
  786.   CRTPutFast(71,1,TimeBuf);
  787.   IF (EdData.Status AND EdStatTextMod) <> 0 THEN
  788.     CRTPutFast(38,1,'*')
  789.   ELSE
  790.     CRTPutFast(38,1,' ');
  791. END;
  792. {$F-}
  793.  
  794.  
  795. {-InitWindow-------------------------------------------------------}
  796. {                                                                  }
  797. { We're not playing with windows here, but since BINED can be run  }
  798. { as a self-windowing editor, the jargon speaks of windows that    }
  799. { simply aren't used in JED.  This proc sets up an editor control  }
  800. { block to receive a new file.  It does NOT read any file into     }
  801. { memory.  It doesn't even know the name of the file to come, only }
  802. { that one must be prepared for.                                   }
  803. {------------------------------------------------------------------}
  804. FUNCTION InitWindow : Boolean;
  805.  
  806. BEGIN
  807.   {Initialize a window for the file}
  808.   ExitCode :=
  809.   InitBinaryEditor(
  810.   EdData,                     { Editor control block }
  811.   MaxFileSize,                { Size of data area to reserve for}
  812.                               { binary editor text buffer, $FFE0 max}
  813.   Windx1,                     { X of upper left corner; 1..80}
  814.   Windy1,                     { Y of upper left corner}
  815.   VisibleX,                   { X of lower right corner}
  816.   VisibleY-1,                 { Y of lower right corner}
  817.   True,                       { True = wait for retrace on CGA cards}
  818.   EdOptInsert+EdOptIndent,    { Initial editor toggles}
  819.   DefaultExtension,           { Default extension for file names}
  820.   ExitCommands,               { Commands which will exit the editor}
  821.   Addr(Clocker));             { Add a clock in the corner}
  822.   CheckInitBinary(ExitCode);
  823.   IF ExitCode = 0 THEN InitWindow := True
  824.     ELSE InitWindow := False;
  825. END;
  826.  
  827.  
  828. {-ReadIntoWindow---------------------------------------------------}
  829. {                                                                  }
  830. { This proc reads the actual workfile into memory and resets the   }
  831. { control block to reflect the new file.                           }
  832. {------------------------------------------------------------------}
  833.  
  834. FUNCTION ReadIntoWindow : Boolean;
  835.  
  836. BEGIN
  837.   { Read the file into memory: }
  838.   ExitCode := ReadFileBinaryEditor(EdData, Fname);
  839.   CheckReadFile(ExitCode,FileNameBinaryEditor(EdData));
  840.   IF ExitCode = 0 THEN
  841.     BEGIN
  842.       ReadIntoWindow := True;
  843.       { Reset the editor for the new file: }
  844.       ResetBinaryEditor(EdData);
  845.     END
  846.   ELSE ReadIntoWindow := False;
  847. END;
  848.  
  849.  
  850. {-FileNameIsValid--------------------------------------------------}
  851. {                                                                  }
  852. { All this does is filter out some of the more blatant ways to     }
  853. { enter a bad filename.  Strings with 0 length are passed along as }
  854. { acceptable, since a zero-length string tells JED to exit to DOS. }
  855. {------------------------------------------------------------------}
  856.  
  857. FUNCTION FileNameIsValid(TempName : String) : Boolean;
  858.  
  859. VAR
  860.   TestFile : FILE;
  861.   I        : Integer;
  862.  
  863. BEGIN
  864.   FileNameIsValid := True;
  865.   IF Length(TempName) < 0 THEN
  866.     FilenameIsValid := False
  867.   ELSE
  868.     IF Length(TempName) > 0 THEN
  869.       IF Pos('.',TempName) > 9 THEN
  870.         FileNameIsValid := False
  871.       ELSE
  872.         BEGIN
  873.           Assign(TestFile,TempName);
  874.           {$I-} Reset(TestFile); {$I+}
  875.           I := IOResult;
  876.           CASE I OF
  877.             0 : Close(TestFile);
  878.             2 : FileNameIsValid := True;
  879.             ELSE FileNameIsValid := False;
  880.           END;  { CASE }
  881.  
  882.         END;
  883. END;
  884.  
  885.  
  886.  
  887. FUNCTION ExecWasSuccessful(ProgName,Parameters : STRING) : Boolean;
  888.  
  889. VAR
  890.   ExecError : Integer;
  891.  
  892. BEGIN
  893.   ExecError := DOSError;
  894.   IF ExecError <> 0 THEN
  895.     BEGIN
  896.  
  897.     END
  898. END;
  899.  
  900.  
  901.  
  902.  
  903. {-ExitBinaryEditor-------------------------------------------------}
  904. {                                                                  }
  905. { This is most important subprogram in the whole system.  When one }
  906. { of a predefined set of "exit commands" is encountered in the     }
  907. { BINED edit stream, BINED lets control return to the caller, with }
  908. { the editor context retained in a largish dfata structure called  }
  909. { EdData.  As long as EdData isn't corrupted, BINED can be re-     }
  910. { entered as though control had never left it.                     }
  911. {                                                                  }
  912. { During these excursions out of BINED, nearly anything can be     }
  913. { done under the illusion that BINED still has control.  On exit,  }
  914. { BINED supplies a code indicating which character sequence caused }
  915. { the exit.  This code can be parsed, and action taken depending   }
  916. { on the exit code.  Each JED command is in fact an exit command,  }
  917. { and everything that JED does apart from pure text editing and    }
  918. { changing edit files is done from subprograms called from within  }
  919. { ExitBinaryEditor.                                                }
  920. {------------------------------------------------------------------}
  921.  
  922. FUNCTION ExitBinaryEditor(VAR EdData  : EdCB;
  923.                           ExitCommand : Integer;
  924.                           VAR Quit    : Boolean) : Boolean;
  925.  
  926. VAR
  927.   ExitCode   : Word;
  928.   FindFile   : SearchRec;
  929.   LineLength : Integer;
  930.   Escape     : Boolean;
  931.   TempName   : String;
  932.  
  933.  
  934. FUNCTION YesAnswer(prompt : string) : Boolean;
  935.      {-Return true for a yes answer to the prompt}
  936. VAR
  937.   ch : Char;
  938.  
  939. BEGIN                     {YesAnswer}
  940.   WriteStatus(prompt);
  941.   REPEAT
  942.     Ch := UpCase(readkey);
  943.   UNTIL ch in ['Y', 'N'];
  944.   Write(ch);
  945.   YesAnswer := (ch = 'Y');
  946. END;                      {YesAnswer}
  947.  
  948.  
  949. PROCEDURE SaveCurrentFile;
  950.  
  951. BEGIN
  952.   CRTPutFast(58,1,'Saving...');
  953.   ExitCode := SaveFileBinaryEditor(EdData, MakeBackup);
  954.   CheckSaveFile(ExitCode, FileNameBinaryEditor(EdData));
  955.   CRTPutFast(58,1,'         ');
  956. END;
  957.  
  958.  
  959.  
  960.   BEGIN                       {ExitBinaryEditor}
  961.     CASE ExitCommand OF
  962.      -1 : BEGIN     { ^K^D: Exit & Save file}
  963.             SaveCurrentFile;
  964.             ExitBinaryEditor := True;
  965.             GoToXY(1,VisibleY);
  966.           END;
  967.  
  968.       0 : BEGIN     { ^K^Q: Exit without saving }
  969.             IF ModifiedFileBinaryEditor(EdData) THEN
  970.               IF YesAnswer('File modified. Save it? (Y/N) ') THEN
  971.                 SaveCurrentFile;
  972.               ExitBinaryEditor := True;
  973.               GoToXY(1,VisibleY);
  974.           END;
  975.  
  976.       1 : BEGIN      { Alt-F: Change current work file }
  977.             SaveCurrentFile;             { Save file's data }
  978.             ConfigData.CursorInset := EdData.CursorPos;
  979.             { The work is done outside the main command loop... }
  980.             ExitBinaryEditor := True
  981.           END;
  982.  
  983.       2 : BEGIN      { Alt-X: Save if necessary and exit }
  984.             IF ModifiedFileBinaryEditor(EdData) THEN
  985.               SaveCurrentFile;
  986.             ExitBinaryEditor := True;
  987.             GotoXY(1,VisibleY);
  988.           END;
  989.  
  990.       3 : BEGIN      { F1: Show help screen }
  991.             ShowHelp;
  992.             ExitBinaryEditor := False
  993.           END;
  994.  
  995.       4 : BEGIN      { F2: Save File }
  996.             SaveCurrentFile;
  997.             ExitBinaryEditor := False
  998.           END;
  999.  
  1000.       5 : BEGIN      { F3: Invoke DEBUG on current .EXE file }
  1001.             IF ModifiedFileBinaryEditor(EdData) THEN
  1002.               SaveCurrentFile;            { If modified, save before EXECing! }
  1003.             SaveScreenOut(JEDScreen);     { Save out JED screen to heap }
  1004.             ClrScr;                       { Clear the screen }
  1005.             WITH ConfigData DO            { Exec to DEBUG with current .EXE }
  1006.               Exec('DEBUG.COM',EXEForm(WorkFile));
  1007.             SaveScreenOut(ExecScreen);    { Save last screen results }
  1008.             WaitForAnyKeystroke;          { Wait for a key press }
  1009.             BringScreenBack(JEDScreen);   { Bring JED screen back from heap }
  1010.             ExitBinaryEditor := False     { And duck back into BINED }
  1011.           END;
  1012.  
  1013.  
  1014.       6 : BEGIN      { F4: Update assemble/link command lines }
  1015.             SaveScreenOut(JEDScreen);
  1016.             ClrScr;
  1017.             GotoXY(17,1);
  1018.             Write('\\JED\\  Assemble/link command edit screen');
  1019.             GotoXY(30,5); Write('Assemble command:');
  1020.             GotoXY(32,9); Write('Link command:');
  1021.             GotoXY(1,13); Writeln('Line editing commands:'); Writeln;
  1022.             Writeln('CR:     Accepts changes and continues');
  1023.             Writeln('ESC:    Abandons changes and continues');
  1024.             Writeln('Ctrl-X: Clears entire field to empty string');
  1025.             Writeln('BS:     Destructive backspace');
  1026.  
  1027.             WITH ConfigData DO
  1028.               BEGIN
  1029.                 GotoXY(1,6); Write(AssembleCommand);
  1030.                 GotoXY(1,10);Write(LinkCommand);
  1031.                 GetString(1,6,AssembleCommand,80,BlackOnWhite,Escape);
  1032.                 GetString(1,10,LinkCommand,80,BlackOnWhite,Escape);
  1033.               END;
  1034.             BringScreenBack(JEDScreen);
  1035.             ExitBinaryEditor := False;
  1036.           END;
  1037.  
  1038.       7 : BEGIN      { F5: Exec to DOS command line }
  1039.             IF ModifiedFileBinaryEditor(EdData) THEN
  1040.               SaveCurrentFile;            { If modified, save before EXECing! }
  1041.             SaveScreenOut(JEDScreen);     { Save out JED screen to heap }
  1042.             ClrScr;                       { Clear the screen }
  1043.             Exec(GetEnv('COMSPEC'),'');   { Execute the DOS command processor }
  1044.             BringScreenBack(JEDScreen);   { Bring JED screen back from heap }
  1045.             ExitBinaryEditor := False     { And duck back into BINED }
  1046.           END;
  1047.  
  1048.       8 : BEGIN      { F6: Examine last Exec screen }
  1049.             SaveScreenOut(JEDScreen);
  1050.             ClrScr;
  1051.             IF ExecScreen.SavePtr <> NIL THEN
  1052.               BEGIN
  1053.                 BringScreenBack(ExecScreen);
  1054.                 SaveScreenOut(ExecScreen);
  1055.               END
  1056.             ELSE     { NIL SavePtr means no Exec screen has been saved yet }
  1057.               BEGIN
  1058.                 GotoXY(12,11);
  1059.                 Writeln('No assemble/link display has been generated yet.');
  1060.                 GotoXY(12,12);
  1061.                 Writeln('Until you assemble or link by pressing F9 or F10,');
  1062.                 GotoXY(12,13);
  1063.                 Writeln('Nothing will be displayable by pressing F6.');
  1064.               END;
  1065.             WaitForAnyKeystroke;
  1066.             BringScreenBack(JEDScreen);
  1067.             ExitBinaryEditor := False
  1068.           END;
  1069.  
  1070.  
  1071.       9 : BEGIN      { F9: Assemble only }
  1072.             IF ModifiedFileBinaryEditor(EdData) THEN
  1073.               SaveCurrentFile;            { If modified, save before EXECing! }
  1074.             SaveScreenOut(JEDScreen);     { Save out JED screen to heap }
  1075.             ClrScr;                       { Clear the screen }
  1076.             WITH ConfigData DO            { Exec to the assembler }
  1077.               BEGIN
  1078.                 Exec(GetProg(AssembleCommand),
  1079.                      GetParms(AssembleCommand,WorkFile));
  1080.                 IF ExecWasSuccessful(GetProg(AssembleCommand),
  1081.                                      GetParms(AssembleCommand,WorkFile))
  1082.                 THEN SaveScreenOut(ExecScreen);  { Save assembler results }
  1083.               END;
  1084.             WaitForAnyKeystroke;          { Wait for a key press }
  1085.             BringScreenBack(JEDScreen);   { Bring JED screen back from heap }
  1086.             ExitBinaryEditor := False     { And duck back into BINED }
  1087.           END;
  1088.  
  1089.       10: BEGIN      { F10: MAKE: Assemble & link (if necessary), and GO }
  1090.             IF ModifiedFileBinaryEditor(EdData) THEN
  1091.               SaveCurrentFile;            { In case we EXEC something ugly }
  1092.             SaveScreenOut(JEDScreen);     { Save out JED screen to heap }
  1093.             ClrScr;
  1094.             { If the workfile has been changed since the last Make, }
  1095.             { *OR* if the .EXE file does not exist on disk, reMake: }
  1096.             IF MustMake(ConfigData.WorkFile) THEN
  1097.               BEGIN
  1098.                 AssembleAndLink;
  1099.                 SaveScreenOut(ExecScreen); { Save assemble/link results }
  1100.                 Exec(EXEForm(ConfigData.WorkFile),ConfigData.TestParms);
  1101.               END
  1102.             ELSE
  1103.               BEGIN
  1104.                 { If it exists, we run it--if not, reMake and run it: }
  1105.                 Exec(EXEForm(ConfigData.WorkFile),ConfigData.TestParms);
  1106.                 IF DOSError <> 0 THEN
  1107.                   BEGIN
  1108.                     AssembleAndLink;
  1109.                     SaveScreenOut(ExecScreen);  { Save assemble/link results }
  1110.                     Exec(EXEForm(ConfigData.WorkFile),ConfigData.TestParms);
  1111.                   END
  1112.               END;
  1113.  
  1114.             WaitForAnyKeystroke;
  1115.             BringScreenBack(JEDScreen);
  1116.             ExitBinaryEditor := False     { And duck back into BINED }
  1117.           END;
  1118.  
  1119.  
  1120.     END; { CASE }
  1121.   END;                        { ExitBinaryEditor }
  1122.  
  1123.  
  1124.  
  1125. {------------------------------------------------------------------}
  1126. {                               JED                                }
  1127. {                        MAIN PROGRAM BLOCK                        }
  1128. {                                                                  }
  1129. {------------------------------------------------------------------}
  1130.  
  1131. BEGIN
  1132.   { The Monochrome Boolean variable is exported by unit TextInfo }
  1133.   { It determines the attribute for the prompt bar: }
  1134.   IF Monochrome THEN BarAttribute := $70  { Inverse video  }
  1135.     ELSE BarAttribute := $1E;             { Yellow on blue }
  1136.  
  1137.   DOSScreen.SavePtr  := NIL; { Make sure all screen pointers are NIL }
  1138.   JEDScreen.SavePtr  := NIL;
  1139.   ExecScreen.SavePtr := NIL;
  1140.  
  1141.   {Begin by saving the current DOS screen onto the heap, }
  1142.   { so that we can restore the screen upon exiting JED.  }
  1143.   SaveScreenOut(DOSScreen);
  1144.   ClrScr;
  1145.  
  1146.   Fname := GetFileName(ConfigData);    { Get a file name }
  1147.   UpdateConfigData := False;           { Don't update until we know }
  1148.                                        { the file is good }
  1149.  
  1150.   {------------------------------------------------------------------}
  1151.   { This is the edit loop; it repeats until the user quits to DOS    }
  1152.   { with Alt-X, Ctrl-KD, or Ctrl-KQ.  On each pass through the loop  }
  1153.   { a different text file is loaded and edited.  The name is gotten  }
  1154.   { from the user via the IF block on the other side of the main     }
  1155.   { command loop; control then loops back here and the file is       }
  1156.   { opened for a new edit.                                           }
  1157.   {------------------------------------------------------------------}
  1158.  
  1159.   REPEAT   { Given a name in FName, This loop loads & edits a file }
  1160.     Quit := False;          { When this becomes True, we exit to DOS }
  1161.     ExitCommand := 0;       { Exit command 0 = quit without saving   }
  1162.  
  1163.     IF InitWindow THEN
  1164.       BEGIN
  1165.         { Read the file into memory: }
  1166.         ExitCode := ReadFileBinaryEditor(EdData, Fname);
  1167.         CheckReadFile(ExitCode, FileNameBinaryEditor(EdData));
  1168.         { Reset the editor for the new file: }
  1169.         ResetBinaryEditor(EdData);
  1170.         { Bined allows us to position the cursor by specifying a byte }
  1171.         { offset into the text file.  We can "remember" this offset & }
  1172.         { set the cursor to it before editing: }
  1173.         EdData.CursorPos := ConfigData.CursorInset;
  1174.       END
  1175.     ELSE
  1176.       BEGIN
  1177.         ShowJEDErrorMessage(5,20,'Not enough heap space to load a file...');
  1178.         WaitForAnyKeystroke;
  1179.         Quit := True;
  1180.       END;
  1181.  
  1182.     VidBlast(TextBufferOrigin,@JEDBar,  { Blast in the JED status line     }
  1183.              VisibleX,VisibleY,         { Dimensions of current screen     }
  1184.              1,VisibleY,                { Load it at bottom screen line    }
  1185.              80,1,                      { JEDBar is 80 wide and 1 high     }
  1186.              $1E,                       { Use the yellow on blue attribute }
  1187.              0);                        { No interspersed blank lines      }
  1188.  
  1189.  
  1190.     {------------------------------------------------------------------}
  1191.     {  This is the main command loop; within this loop a single file   }
  1192.     {  is edited.                                                      }
  1193.     {------------------------------------------------------------------}
  1194.  
  1195.     WHILE NOT Quit DO
  1196.       BEGIN
  1197.         ExitCommand :=
  1198.         UseBinaryEditor(
  1199.         EdData,                   { Editor control block for this window }
  1200.         '');                      { No startup commands passed to editor }
  1201.         Quit := ExitBinaryEditor(EdData,ExitCommand,Quit); { Parse commands }
  1202.       END;
  1203.  
  1204.     {------------------------------------------------------------------}
  1205.     {  End main command loop                                           }
  1206.     {------------------------------------------------------------------}
  1207.  
  1208.     { We've finished with the file being edited in the loop above; now }
  1209.     { release the heap space used by the editor text buffer and data   }
  1210.     { structure: }
  1211.     ReleaseBinaryEditorHeap(EdData);
  1212.  
  1213.     {------------------------------------------------------------------}
  1214.     {  This IF statement handles changing of the current work file.    }
  1215.     {  By this point the old file has been disposed from the heap and  }
  1216.     {  a new file needs to be identified and opened.  The file is only }
  1217.     {  *identified* here; the file is opened and loaded with the same  }
  1218.     {  code that does it to the original file loaded when JED first    }
  1219.     {  begins executing
  1220.     {------------------------------------------------------------------}
  1221.  
  1222.     IF ExitCommand = 1 THEN       { IF Alt-F was pressed, change file }
  1223.       BEGIN
  1224.         { Prompt the user for a new file name: }
  1225.         REPEAT
  1226.           TempName := RequestFileName(ConfigData);
  1227.           IF Pos('.',TempName) = 0 THEN
  1228.             TempName := TempName + DefaultExtension;
  1229.         UNTIL FileNameIsValid(TempName);   { Make sure it's a valid name }
  1230.         IF Length(TempName) <= 0 THEN Quit := True      { Quit to DOS }
  1231.           ELSE
  1232.         BEGIN                     { Otherwise, assert new filename }
  1233.           FName := ForceCase(UP,TempName);
  1234.           IF FName <> Configdata.WorkFile THEN { If same file, skip update }
  1235.           WITH ConfigData DO      { Otherwise, update configuration record }
  1236.             BEGIN
  1237.               WorkFile := FName;
  1238.               CursorInset := 0;
  1239.             END;
  1240.           Quit := False           { And loop back & work on new file }
  1241.         END
  1242.       END;
  1243.  
  1244.   UNTIL Quit;  { When we hit this point and Quit is True, it's back to DOS }
  1245.  
  1246.   { Save the updated configuration data to disk: }
  1247.   IF UpdateConfigData THEN SaveConfigFile(ConfigData);
  1248.  
  1249.   { Finally, we restore the DOS screen saved on the heap before we began: }
  1250.   BringScreenBack(DOSScreen)
  1251.  
  1252. END.
  1253.